package com.yanqu.road.server.manger.marriage;

import com.alibaba.fastjson.JSON;
import com.yanqu.road.entity.banquet.invitation.BanquetChildInvitation;
import com.yanqu.road.entity.log.LogMarriageReward;
import com.yanqu.road.entity.log.LogPublishMarriageApply;
import com.yanqu.road.entity.task.args.union.UnionActivityTaskArgs;
import com.yanqu.road.logic.bussiness.marriage.MarriageBusiness;
import com.yanqu.road.logic.pb.MarriagePb;
import com.yanqu.road.logic.config.GameConfig;
import com.yanqu.road.entity.config.system.NpcInfo;
import com.yanqu.road.entity.enums.*;
import com.yanqu.road.entity.marriage.CrossMarriageResult;
import com.yanqu.road.entity.marriage.MarriageInfo;
import com.yanqu.road.entity.marriage.MarriageRecord;
import com.yanqu.road.entity.marriage.MarriageResult;
import com.yanqu.road.entity.player.UserChildSite;
import com.yanqu.road.entity.player.UserInfo;
import com.yanqu.road.entity.player.UserMarriageData;
import com.yanqu.road.entity.servercenter.ServerInfo;
import com.yanqu.road.logic.thread.ThreadTaskManger;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.server.manger.log.AutoLogMgr;
import com.yanqu.road.server.manger.player.BlacklistMgr;
import com.yanqu.road.utils.ConfigHelper;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.pb.marriage.CrossMarriageProto;
import com.yanqu.road.pb.marriage.MarriageProto;
import com.yanqu.road.server.GameServer;
import com.yanqu.road.server.TempMgr;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.banquet.BanquetModule;
import com.yanqu.road.server.gameplayer.module.player.*;
import com.yanqu.road.server.gameplayer.module.xiaomian.XiaoMianModule;
import com.yanqu.road.server.logic.mail.MailManager;
import com.yanqu.road.server.logic.player.NpcManager;
import com.yanqu.road.server.manger.*;
import com.yanqu.road.server.manger.banquet.BanquetConfigMgr;
import com.yanqu.road.server.manger.config.ChildMgr;
import com.yanqu.road.server.manger.config.TitleMgr;
import com.yanqu.road.server.manger.log.LogMgr;
import com.yanqu.road.server.manger.marriage.task.PartnerMarrySuccessTask;
import com.yanqu.road.server.manger.player.UserMgr;
import com.yanqu.road.server.manger.player.GamePlayerMgr;
import com.yanqu.road.server.manger.relatives.RelativesMgr;
import com.yanqu.road.server.manger.relatives.task.AddCrossMarryTimesTask;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.UUIDHelper;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 玩家联姻管理类
 */
public class MarriageMgr extends TempMgr {

    private static Logger logger = LogManager.getLogger(MarriageMgr.class.getName());

    private static Map<Long, Map<String, MarriageInfo>> marriageInfoMap;      //成年子嗣集合
    /**
     * 全服发布的联姻请求
     */
    private static Map<String, MarriageInfo> serverApplyMap;
    /**
     * 指定的联姻请求
     * key: 指定人  value： 所有联姻请求
     */
    private static Map<Long, Map<String, MarriageInfo>> appointApplyMap;

    //缓存玩家的本服列表
    //userId sexType
    private static Map<Long, Map<Integer, Map<String, MarriageInfo>>> userServerApplyMap = new ConcurrentHashMap<>();

    private static Map<Long, UserMarriageData> userMarriageDataMap;

    private static RandomHelper random = new RandomHelper();

    private static Object locker = new Object();

    private static ThreadTaskManger threadTaskManger;

    @Override
    public boolean reloadData() {
        if(null == threadTaskManger) {
            threadTaskManger = new ThreadTaskManger(1, "MarriageTaskManger");
        }
        userMarriageDataMap = MarriageBusiness.getUserMarriageDataMap();
        marriageInfoMap = MarriageBusiness.getMarriageInfoMap(GameServer.getInstance().getServerId());
        initMarriageApply();
        return true;
    }

    @Override
    public boolean init() throws Exception {
        boolean isReload = reload();
        if(isReload) {//重载成功
            groupChange();
        }

        //监听商会分组变更
        CrossUnionServerGroupMgr.addReloadListener(() -> unionGroupChange());
        return isReload;
    }

    @Override
    public boolean reloadConfig() throws Exception {
        return true;
    }

    @Override
    public boolean stop() {
        return false;
    }

    @Override
    public boolean save() {
        List<MarriageInfo> needRemoveList = new ArrayList<>();
        for (Map<String, MarriageInfo> entry : marriageInfoMap.values()) {
            for (MarriageInfo info : entry.values()) {
                if (info.isInsertOption()) {
                    MarriageBusiness.addMarriageInfo(info);
                } else if (info.isUpdateOption()) {
                    MarriageBusiness.updateMarriageInfo(info);
                }
                if (info.getStatus() == eMarriageStatusType.MARRIED && !info.isDirty()) {
                    needRemoveList.add(info);
                }
            }
        }
        for (MarriageInfo info : needRemoveList) {
            Map<String, MarriageInfo> infoMap = marriageInfoMap.get(info.getUserId());
            if (null != infoMap) {
                infoMap.remove(info.getChildUid());
            }
        }
        return true;
    }

    public static void setUserMarriageData(UserMarriageData userMarriageData){
        if(null != userMarriageData) {
            userMarriageDataMap.put(userMarriageData.getUserId(), userMarriageData);
        }
    }

    private void initMarriageApply() {
        long serverId = ConfigHelper.getLong("serverId");
        int groupCount = CrossUnionServerGroupMgr.getGroupServerCountByServerId(serverId);
        synchronized (locker) {
            Map<String, MarriageInfo> tempServerApplyMap = new ConcurrentHashMap<>();
            Map<Long, Map<String, MarriageInfo>> tempAppointApplyMap = new ConcurrentHashMap<>();
            for (Map<String, MarriageInfo> marriageInfoMap : marriageInfoMap.values()) {
                for (MarriageInfo data : marriageInfoMap.values()) {
                    if (data.getStatus() == eMarriageStatusType.APPLY || data.getStatus() == eMarriageStatusType.CrossWorldPublish) {//如果是发布联姻状态
                        if (MarriageMgr.isOverApplyTime(data)) {
                            data.setStatus(eMarriageStatusType.WAIT);
                            data.setAppointUserId(0);
                        } else {
                            if (data.getAppointUserId() > 0) {// 指定联姻
                                if (!tempAppointApplyMap.containsKey(data.getAppointUserId())) {
                                    tempAppointApplyMap.put(data.getAppointUserId(), new ConcurrentHashMap<>());
                                }
                                tempAppointApplyMap.get(data.getAppointUserId()).put(data.getChildUid(), data);
                            } else {
                                if(groupCount > 1 && data.getStatus() == eMarriageStatusType.APPLY && data.getAppointUserId() == 0){
                                    //纠正之前的数据
                                    data.setStatus(eMarriageStatusType.WAIT);
                                }
                                if(data.getStatus() == eMarriageStatusType.CrossWorldPublish || data.getStatus() == eMarriageStatusType.APPLY){
                                    tempServerApplyMap.put(data.getChildUid(), data);
                                }
                            }
                        }
                    }
                }
            }
            serverApplyMap = tempServerApplyMap;
            appointApplyMap = tempAppointApplyMap;
        }

    }

    /**
     * 添加成年子女
     */
    public static MarriageInfo addToMarriage(GamePlayer player, UserChildSite userChildSite, Property reward, BigInteger childEarnSpeed, boolean isLonely,
                                     double childOccupationAddition, int childOccupationId,int position) {
        MarriageInfo marriageInfo = initAdultChild(userChildSite, reward, childEarnSpeed, isLonely, childOccupationAddition, childOccupationId,position);
        if(isLonely){
            player.getModule(MarriageModule.class).addLonelyMarriage(marriageInfo);
            //DataAnalyticsMgr.trackMarriageDieAlone(player, marriageInfo);
        }else {
            if (!marriageInfoMap.containsKey(player.getUserId())) {
                marriageInfoMap.put(player.getUserId(), new ConcurrentHashMap<>());
            }
            marriageInfoMap.get(marriageInfo.getUserId()).put(marriageInfo.getChildUid(), marriageInfo);
            player.getModule(MarriageModule.class).syncUnMarriageChild(marriageInfo);
        }
        UserMarriageData userMarriageData = player.getModule(MarriageModule.class).getUserMarriageData();
        if(childEarnSpeed.compareTo(userMarriageData.getMaxChildEarnSpeed()) > 0){
            userMarriageData.setMaxChildEarnSpeed(childEarnSpeed);
            player.notifyListener(eGamePlayerEventType.ChildEarnSpeed.getValue(), userMarriageData);
        }

        //生成状元贴
        if(BanquetConfigMgr.isChildInvitationOccupation(marriageInfo.getChildOccupationId())){
            BanquetChildInvitation inv = new BanquetChildInvitation();
            inv.setChildUid(marriageInfo.getChildUid());
            inv.setChildTalent(marriageInfo.getChildTalent());
            inv.setChildOccupation(marriageInfo.getChildOccupation());
            inv.setChildOccupationId(marriageInfo.getChildOccupationId());
            inv.setChildName(marriageInfo.getChildName());
            inv.setChildSex(marriageInfo.getChildSex());
            inv.setChildEarnSpeed(marriageInfo.getChildEarnSpeed().toString());
            inv.setIsTwins(marriageInfo.isTwins());
            player.getModule(BanquetModule.class).makeInvitation(eBanquetInvitationType.ChildInvitation.getValue(), JSON.toJSONString(inv));
        }
        return marriageInfo;
    }

    /**
     * 初始化成年子女(根据摇篮的类型也就是位置1和2是双胞胎的成年，0是普通的)
     */
    public static MarriageInfo initAdultChild(UserChildSite userChildSite, Property reward, BigInteger childEarnSpeed, boolean isLonely,
                                              double childOccupationAddition, int childOccupationId,int position) {
        UserInfo userInfo = UserMgr.getUserInfo(userChildSite.getUserId());
        MarriageInfo marriageInfo = new MarriageInfo();
        marriageInfo.setUserId(userChildSite.getUserId());
        marriageInfo.setUserNickName(userInfo.getNickName());
        if(position == 2){
            //双胞胎第二个两个Uid
            marriageInfo.setChildUid(userChildSite.getChildUid()+"-2");
            //记录一下子嗣成长赚速
            marriageInfo.setGrowEarnSpeed(userChildSite.getGrowAddEarnSpeed2());
        }else{
            marriageInfo.setChildUid(userChildSite.getChildUid());
            //记录一下子嗣成长赚速
            marriageInfo.setGrowEarnSpeed(userChildSite.getGrowAddEarnSpeed());
        }
        marriageInfo.setMotherBeautyId(userChildSite.getMotherBeautyId());
        marriageInfo.setChildTalent(userChildSite.getChildTalent());
        marriageInfo.setChildSex(ChildMgr.getChildSexByPosition(userChildSite,position));
        marriageInfo.setChildOccupation(userChildSite.getChildOccupation());
        //双胞胎成年第二个位置用的第二个名字
        if(position == 2){
            marriageInfo.setChildName(userChildSite.getChildName2());
        }else{
            marriageInfo.setChildName(userChildSite.getChildName());
        }
        marriageInfo.setChildHeadIcon(userChildSite.getChildHeadIcon());
        marriageInfo.setChildAdultTime(System.currentTimeMillis() / 1000);
        if(isLonely){
            marriageInfo.setStatus(eMarriageStatusType.LONELY);
        }else {
            marriageInfo.setStatus(eMarriageStatusType.WAIT);
        }
        marriageInfo.setReward(reward);
        marriageInfo.setChildOccupationId(childOccupationId);
        marriageInfo.setChildEarnSpeed(childEarnSpeed);
        marriageInfo.setChildOccupationAddition((int) childOccupationAddition);
        if(position == 0){
            marriageInfo.setTwins(false);
        }else {
            marriageInfo.setTwins(true);
        }
        marriageInfo.setServerId(GameServer.getInstance().getServerId());
        marriageInfo.setInsertOption();
        return marriageInfo;
    }

    /**
     * 初始化NPC联姻信息
     */
    private static MarriageInfo initNpcMarriage(GamePlayer player, int childSex) {
        long nowTime = System.currentTimeMillis() / 1000;
        MarriageInfo marriageInfo = new MarriageInfo();
        marriageInfo.setUserId(-1);
        NpcInfo npcInfo = NpcManager.getRandomNpc(player.getLanguage());
        marriageInfo.setUserNickName(npcInfo.getNickName());
        marriageInfo.setChildUid(UUIDHelper.randomUUID(String.valueOf(GameServer.getInstance().getServerId())));
        marriageInfo.setChildTalent(ChildMgr.getMinChildTalent());
        marriageInfo.setChildSex(childSex);
        marriageInfo.setChildOccupation(ConfigMgr.getRandomChildOccupation(Integer.MAX_VALUE));
        marriageInfo.setChildName(ChildMgr.getRandomChildName(childSex, player.getLanguage()));
        marriageInfo.setChildHeadIcon(ChildMgr.getChildHeadIcon(childSex));
        marriageInfo.setChildAdultTime(nowTime);
        marriageInfo.setStatus(eMarriageStatusType.APPLY);
        marriageInfo.setPublishTime(nowTime);
        Property reward = ChildMgr.getChildAdultReward(marriageInfo.getChildTalent(), marriageInfo.getChildOccupation(), false);
        marriageInfo.setReward(reward);
        marriageInfo.setChildOccupationId(ChildMgr.getRandomOccupationId(marriageInfo.getChildOccupation(), marriageInfo.getChildSex(), false));
        int childOccupationAddition = ChildMgr.getChildOccupationAddition(marriageInfo.getChildOccupationId());
        //机器人亲密度为0
        int intimacy = GameConfig.ROBOT_MOTHER_INTIMACY;
        marriageInfo.setChildEarnSpeed(ChildMgr.getChildAdultEarnSpeed(null, ChildMgr.getMinChildTrainType(), marriageInfo.getChildTalent(),
                marriageInfo.getChildOccupation(), childOccupationAddition, intimacy,false, marriageInfo.getChildSex(), marriageInfo));
        marriageInfo.setServerId(GameServer.getInstance().getServerId());
        marriageInfo.setInsertOption();
        return marriageInfo;
    }

    /**
     * 发布联姻请求
     */
    public static int publishMarriageApply(GamePlayer player, String childUid, long appointUserId, boolean isAllPublish, int type) {
        if(!marriageInfoMap.containsKey(player.getUserId())){
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }
        if(isAllPublish){
            return allPublish(player, appointUserId);
        }else {
            return normalPublish(player, childUid, appointUserId, type);
        }
    }

    /**
     * 全部发布指向联姻
     */
    private static int allPublish(GamePlayer player, long appointUserId){
        UserInfo userInfo = UserMgr.getUserInfo(appointUserId);
        if (null == userInfo) {
            return GameErrorCode.E_USER_FORBIDDEN;
        }
        if(BlacklistMgr.isInLocalBlacklist(appointUserId, eBlacklistType.Marriage.getValue(), player.getUserId())){
            return GameErrorCode.E_IN_PLAYER_BLACKLIST;
        }
        List<MarriageInfo> waitList = getWaitPublishList(player.getUserId());
        if(waitList.size() <= 0){
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }
        int result = checkAppointMarriage(appointUserId);
        if(0 != result){
            return result;
        }
        long nowTime = System.currentTimeMillis() / 1000;
        List<MarriageInfo> sendList = new ArrayList<>();
        for(MarriageInfo marriageInfo : waitList){
            if(getAppointApply(appointUserId).size() >= GameConfig.MARRIAGE_MAX_APPOINT_APPLY_COUNT){
                break;
            }
            marriageInfo.setAppointUserId(appointUserId);
            synchronized (locker) {
                if(marriageInfo.getStatus() != eMarriageStatusType.WAIT){
                    continue;
                }
                marriageInfo.setStatus(eMarriageStatusType.APPLY);
            }
            marriageInfo.setPublishTime(nowTime);
            appointApplyMap.get(appointUserId).put(marriageInfo.getChildUid(), marriageInfo);
            sendList.add(marriageInfo);
            //增加发布日志
            AutoLogMgr.add(new LogPublishMarriageApply(player.getUserId(), marriageInfo.getChildUid(), marriageInfo.getChildName(), false,
                    BigInteger.ZERO));
        }
        if(sendList.size() > 0) {
            //推送发布请求的子嗣
            player.getModule(MarriageModule.class).syncUnMarriageChild(sendList, false);
            GamePlayer appointPlayer = GamePlayerMgr.getOnlinePlayer(appointUserId);
            if (null != appointPlayer) {
                //红点通知
                appointPlayer.getModule(MarriageModule.class).syncMarriageApply(sendList, false);
                UserMarriageData appointMarriageData = getUserMarriageData(appointUserId);
                if(null != appointMarriageData) {
                    appointMarriageData.setLastMarriageApplyNotifyTime(nowTime);
                }
            }
        }
        return 0;
    }

    private static int checkAppointMarriage(long appointUserId) {
        UserMarriageData appointData = getUserMarriageData(appointUserId);
        if(null == appointData){
            return GameErrorCode.E_MARRIAGE_PARTNER_NO_OPEN;
        }
        if(!appointData.isAcceptAppoint()){
            return GameErrorCode.E_MARRIAGE_NO_ACCEPT_APPOINT;
        }
        if (!appointApplyMap.containsKey(appointUserId)) {
            appointApplyMap.put(appointUserId, new ConcurrentHashMap<>());
        }
        if(getAppointApply(appointUserId).size() >= GameConfig.MARRIAGE_MAX_APPOINT_APPLY_COUNT){
            return GameErrorCode.E_MARRIAGE_APPOINT_APPLY_MAX;
        }
        return 0;
    }


    private static int normalPublish(GamePlayer player, String childUid, long appointUserId, int type){
        MarriageInfo marriageInfo = getAdultChild(player.getUserId(), childUid);
        if (null == marriageInfo) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if (marriageInfo.getStatus() != eMarriageStatusType.WAIT) {// 已经申请 或者 已经完成
            return GameErrorCode.E_MARRIAGE_STATE_NO_WAIT;
        }

        if(1 == type){//发布商会联姻
            return normalPublishUnion(player, marriageInfo);
        }else {
            if (appointUserId > 0) {// 指定玩家发布
                return normalPublishAppoint(player, appointUserId, marriageInfo);
            } else {// 全服发布
                return normalPublishServer(player, marriageInfo);
            }
        }
    }

    /**
     * 发布指定联姻
     * @param player
     * @param appointUserId
     * @param marriageInfo
     * @return
     */
    private static int normalPublishAppoint(GamePlayer player, long appointUserId, MarriageInfo marriageInfo){
        UserInfo userInfo = UserMgr.getUserInfo(appointUserId);
        if (null == userInfo) {
            return GameErrorCode.E_USER_FORBIDDEN;
        }
        if(BlacklistMgr.isInLocalBlacklist(appointUserId, eBlacklistType.Marriage.getValue(), player.getUserId())){
            return GameErrorCode.E_IN_PLAYER_BLACKLIST;
        }
        int result = checkAppointMarriage(appointUserId);
        if(0 != result){
            return result;
        }
        long nowTime = System.currentTimeMillis() / 1000;
        synchronized (locker) {
            if (marriageInfo.getStatus() != eMarriageStatusType.WAIT) {// 已经申请 或者 已经完成
                return GameErrorCode.E_MARRIAGE_STATE_NO_WAIT;
            }
            marriageInfo.setStatus(eMarriageStatusType.APPLY);
            marriageInfo.setAppointUserId(appointUserId);
        }
        marriageInfo.setPublishTime(nowTime);
        appointApplyMap.get(appointUserId).put(marriageInfo.getChildUid(), marriageInfo);
        GamePlayer appointPlayer = GamePlayerMgr.getOnlinePlayer(appointUserId);
        if(null != appointPlayer) {//红点通知
            appointPlayer.getModule(MarriageModule.class).syncMarriageApply(marriageInfo);
        }
        player.getModule(MarriageModule.class).syncUnMarriageChild(marriageInfo);
        //增加发布日志
        AutoLogMgr.add(new LogPublishMarriageApply(player.getUserId(), marriageInfo.getChildUid(), marriageInfo.getChildName(), false,
                BigInteger.ZERO));
        return 0;
    }

    /**
     * 发布全服联姻
     * @param player
     * @param marriageInfo
     * @return
     */
    private static int normalPublishServer(GamePlayer player, MarriageInfo marriageInfo){
        synchronized (locker) {
            if (marriageInfo.getStatus() != eMarriageStatusType.WAIT) {// 已经申请 或者 已经完成
                return GameErrorCode.E_MARRIAGE_STATE_NO_WAIT;
            }
            //开启了商会分组
            long serverId = ConfigHelper.getLong("serverId");
            if(CrossUnionServerGroupMgr.getGroupServerCountByServerId(serverId) > 1){

                //跨服发布，若是跨服未响应，则状态脏了
                CrossMarriageProto.CrossMarriageApplyPublishCrossReqMsg.Builder pushMsg = CrossMarriageProto.CrossMarriageApplyPublishCrossReqMsg.newBuilder();
                MarriageProto.MarriageApplyTempMsg.Builder applyData = MarriagePb.parseMarriageApplyTempMsg(marriageInfo);
                //跨服状态初始都是已发布
                applyData.setState(eMarriageStatusType.CrossWorldPublish.getValue());
                applyData.setPublishTime(System.currentTimeMillis()/1000);
                pushMsg.setApplyData(applyData);
                pushMsg.setLimitEarnSpeed("0");
                pushMsg.setAppointUserId(0);
                pushMsg.setPartnerServerId(0);
                YanQuMessage pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_CROSS_WORLD_MARRIAGE_APPLY_PUBLISH, pushMsg);
                GamePlayerMgr.sendPacket(player.getUserId(), pbMsg);

                //临时设置时间，中间状态
                marriageInfo.setPublishTime(System.currentTimeMillis() / 1000);
                marriageInfo.setStatus(eMarriageStatusType.WaitCrossWorldPublish);
                marriageInfo.setAppointUserId(0);
                return -1;
            }else {
                marriageInfo.setStatus(eMarriageStatusType.APPLY);
                marriageInfo.setAppointUserId(0);
            }
        }
        marriageInfo.setPublishTime(System.currentTimeMillis() / 1000);
        serverApplyMap.put(marriageInfo.getChildUid(), marriageInfo);
        long totalPublishTimes = player.getModule(AttributeModule.class).getAttribute(ePlayerAttrType.PublishServerMarriageApplyTotalTimes);
        totalPublishTimes++;
        player.getModule(AttributeModule.class).setAttribute(ePlayerAttrType.PublishServerMarriageApplyTotalTimes, totalPublishTimes);
        //任务、成就
        player.notifyListener(eGamePlayerEventType.PublishServerMarriageApplyTotalTimes.getValue(), totalPublishTimes);
        player.getModule(MarriageModule.class).syncUnMarriageChild(marriageInfo);
        //增加发布日志
        AutoLogMgr.add(new LogPublishMarriageApply(player.getUserId(), marriageInfo.getChildUid(), marriageInfo.getChildName(), false,
                BigInteger.ZERO));
        return 0;
    }

    /**
     * 本服联姻发布后响应（商会分组跨服）
     */
    public static int crossWorldMarriagePublishResponse(GamePlayer player, CrossMarriageProto.CrossMarriageApplyPublishCrossRespMsg reqMsg){
        MarriageInfo marriageInfo = getAdultChild(player.getUserId(), reqMsg.getChildUid());
        if (null == marriageInfo) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }
        synchronized (locker) {
            if (marriageInfo.getStatus() != eMarriageStatusType.WaitCrossWorldPublish) {//状态不对
                return GameErrorCode.E_CROSS_MARRIAGE_STATE_HAS_CHANGE;
            }
            if(reqMsg.getRet() == 0) {
                marriageInfo.setPublishTime(reqMsg.getPublishTime());
                marriageInfo.setStatus(eMarriageStatusType.CrossWorldPublish);
                player.getModule(MarriageModule.class).syncUnMarriageChild(marriageInfo);
                //增加发布日志
                AutoLogMgr.add(new LogPublishMarriageApply(player.getUserId(), marriageInfo.getChildUid(), marriageInfo.getChildName(), true,
                        marriageInfo.getLimitEarnSpeed()));
            }else {
                marriageInfo.setStatus(eMarriageStatusType.WAIT);
            }
        }
        if(reqMsg.getRet() == 0){
            serverApplyMap.put(marriageInfo.getChildUid(), marriageInfo);
            long totalPublishTimes = player.getModule(AttributeModule.class).getAttribute(ePlayerAttrType.PublishServerMarriageApplyTotalTimes);
            totalPublishTimes++;
            player.getModule(AttributeModule.class).setAttribute(ePlayerAttrType.PublishServerMarriageApplyTotalTimes, totalPublishTimes);
            //任务、成就
            player.notifyListener(eGamePlayerEventType.PublishServerMarriageApplyTotalTimes.getValue(), totalPublishTimes);
        }
        return reqMsg.getRet();
    }

    /**
     * 发布商会联姻
     * @param player
     * @param marriageInfo
     * @return
     */
    private static int normalPublishUnion(GamePlayer player, MarriageInfo marriageInfo){
        String unionUid = player.getUserInfo().getUnionUid();
        if(StringUtils.isNullOrEmpty(unionUid)){
            return GameErrorCode.E_UNION_NO_JOIN_UNION;
        }
        synchronized (locker) {
            if (marriageInfo.getStatus() != eMarriageStatusType.WAIT) {// 已经申请 或者 已经完成
                return GameErrorCode.E_MARRIAGE_STATE_NO_WAIT;
            }
            if(getUserPublishUnionApplyCount(player.getUserId()) >= GameConfig.UNION_CHILDREN_APPLY_MARRIAGE_LIMIT_MAX_COUNT){
                return GameErrorCode.E_MARRIAGE_UNION_APPLY_COUNT_OVER;
            }
            marriageInfo.setStatus(eMarriageStatusType.WaitCrossPublish);
            marriageInfo.setAppointUserId(0);
            marriageInfo.setPublishTime(System.currentTimeMillis() / 1000);
        }

        //发送到跨服
        CrossMarriageProto.CrossMarriageApplyPublishUnionReqMsg.Builder pushMsg = CrossMarriageProto.CrossMarriageApplyPublishUnionReqMsg.newBuilder();
        pushMsg.setApplyData(MarriagePb.parseMarriageApplyTempMsg(marriageInfo));
        YanQuMessage pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_PUBLISH_MARRIAGE_UNION, pushMsg);
        GamePlayerMgr.sendPacket(player.getUserId(), pbMsg);
        return 0;
    }

    /**
     * 同意全服联姻（开启商会分组）
     */
    public static MarriageResult dealCrossWorldMarriage(GamePlayer player, String myChildUid, String partnerChildUid, long partnerServerId){
        long serverId = ConfigHelper.getLong("serverId");
        //如果是本服
        if(serverId == partnerServerId){
            MarriageResult result = dealServerMarriage(player, myChildUid, partnerChildUid);
            if(result.getRet() == 0){
                //跨服清除发布状态，清除对方状态
                //此时，我的还未发布跨服
                MarriageProto.MarriageApplyCancelReqMsg.Builder builder = MarriageProto.MarriageApplyCancelReqMsg.newBuilder();
                builder.setChildUid(partnerChildUid);
                player.sendPacket(Protocol.C_CROSS_WORLD_CLEAR_MARRIAGE_STATE, builder);
            }
            return result;
        }
        MarriageInfo myChild = getAdultChild(player.getUserId(), myChildUid);
        if(myChild == null){
            MarriageResult result = new MarriageResult();
            result.setRet(GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND);
            return result;
        }
        //到对方区服检查状态
        CrossMarriageProto.CrossMarriageAgreeCrossDealReqMsg.Builder reqMsg = CrossMarriageProto.CrossMarriageAgreeCrossDealReqMsg.newBuilder();
        reqMsg.setApplyData(MarriagePb.parseMarriageApplyTempMsg(myChild));
        reqMsg.setPartnerChildUid(partnerChildUid);
        player.sendPacket(Protocol.C_CROSS_WORLD_MARRIAGE_APPLY_AGREE, reqMsg);
        MarriageResult result = new MarriageResult();
        result.setRet(-1);
        return result;
    }

    /**
     * 同意全服联姻
     */
    public static MarriageResult dealServerMarriage(GamePlayer player, String myChildUid, String partnerChildUid) {
        MarriageInfo myChild = getAdultChild(player.getUserId(), myChildUid);
        MarriageInfo partnerChild = serverApplyMap.get(partnerChildUid);
        MarriageResult result = dealMarriage(player, myChild, partnerChild);
        if (result.getRet() != 0) {
            return result;
        }
        serverApplyMap.remove(partnerChildUid);
        return result;
    }

    /**
     * 处理指向联姻
     */
    public static MarriageResult dealAppointMarriage(GamePlayer player, String myChildUid, String partnerChildUid, boolean agree) {
        if(!appointApplyMap.containsKey(player.getUserId())){
            MarriageResult result = new MarriageResult();
            result.setRet(GameErrorCode.E_MARRIAGE_APPLY_NO_FOUND);
            return result;
        }
        MarriageInfo myChild = getAdultChild(player.getUserId(), myChildUid);
        MarriageInfo partnerChild = appointApplyMap.get(player.getUserId()).get(partnerChildUid);
        if(null == partnerChild){
            MarriageResult result = new MarriageResult();
            result.setRet(GameErrorCode.E_MARRIAGE_APPLY_NO_FOUND);
            return result;
        }
        // 开始处理
        if (agree) {
            MarriageResult result = dealMarriage(player, myChild, partnerChild);
            if (result.getRet() != 0) {
                return result;
            }
            appointApplyMap.get(player.getUserId()).remove(partnerChildUid);
            return result;
        } else {
            synchronized (locker) {
                if(partnerChild.getStatus() == eMarriageStatusType.APPLY) {
                    partnerChild.setStatus(eMarriageStatusType.WAIT);
                }
            }
            partnerChild.setAppointUserId(0);
            appointApplyMap.get(player.getUserId()).remove(partnerChildUid);
            GamePlayer partnerPlayer = GamePlayerMgr.getOnlinePlayer(partnerChild.getUserId());
            if(null != partnerPlayer){
                partnerPlayer.getModule(MarriageModule.class).marriageRefuseNotify(partnerChildUid);
            }
            return new MarriageResult();
        }
    }

    /**
     * 一键拒绝所有指向联姻
     */
    public static MarriageResult refuseAllAppointMarriage(GamePlayer player) {
        MarriageResult result = new MarriageResult();
        if(appointApplyMap.containsKey(player.getUserId())){
            List<MarriageInfo> partnerChildList = new ArrayList<>(appointApplyMap.get(player.getUserId()).values());
            List<MarriageInfo> needRemoveList = new ArrayList<>();
            for(MarriageInfo partnerChild : partnerChildList){
                synchronized (locker) {
                    if(partnerChild.getStatus() == eMarriageStatusType.APPLY) {
                        partnerChild.setStatus(eMarriageStatusType.WAIT);
                    }
                }
                partnerChild.setAppointUserId(0);
                needRemoveList.add(partnerChild);
            }
            if(needRemoveList.size() > 0) {
                Map<Long, List<String>> refuseMap = new ConcurrentHashMap<>();
                for(MarriageInfo partnerChild : needRemoveList){
                    appointApplyMap.get(player.getUserId()).remove(partnerChild.getChildUid());
                    if(!refuseMap.containsKey(partnerChild.getUserId())){
                        refuseMap.put(partnerChild.getUserId(), new ArrayList<>());
                    }
                    refuseMap.get(partnerChild.getUserId()).add(partnerChild.getChildUid());
                }
                player.getModule(MarriageModule.class).syncMarriageApply(getAppointApply(player.getUserId()), true);
                for(Map.Entry<Long, List<String>> dataEntry : refuseMap.entrySet()){
                    GamePlayer partnerPlayer = GamePlayerMgr.getOnlinePlayer(dataEntry.getKey());
                    if(null != partnerPlayer){//通知拒绝
                        partnerPlayer.getModule(MarriageModule.class).marriageRefuseNotify(dataEntry.getValue());
                    }
                }
            }
        }
        return result;
    }

    /**
     * 获取指向联姻列表
     */
    public static List<MarriageInfo> getAppointApply(long userId) {
        List<MarriageInfo> list = new ArrayList<>();
        Map<String, MarriageInfo> applyMap = appointApplyMap.get(userId);
        if (null != applyMap) {
            List<MarriageInfo> applyList = new ArrayList<>(applyMap.values());
            for (MarriageInfo marriageInfo : applyList) {
                if(marriageInfo.getStatus() == eMarriageStatusType.APPLY) {
                    if (!isOverApplyTime(marriageInfo)) {
                        list.add(marriageInfo);
                    }
                }else {//不是发布状态
                    applyMap.remove(marriageInfo.getChildUid());
                }
            }
        }
        return list;
    }

    /**
     * 获取全服联姻列表, 随机获取5个
     */
    public static List<MarriageInfo> getServerApplyList(GamePlayer player, int childSex) {
        List<MarriageInfo> applyList = new ArrayList<>();
        if(userServerApplyMap.containsKey(player.getUserId())) {
            Map<Integer, Map<String, MarriageInfo>> userApplyMap = userServerApplyMap.get(player.getUserId());
            if(userApplyMap.containsKey(childSex)) {
                Map<String, MarriageInfo> sexApplyMap = userApplyMap.get(childSex);
                for (MarriageInfo marriageInfo : sexApplyMap.values()) {
                    if (marriageInfo.getStatus() != eMarriageStatusType.APPLY || isOverApplyTime(marriageInfo) ||
                            !serverApplyMap.containsKey(marriageInfo.getChildUid())) {//过期或者已联姻的移除掉
                        sexApplyMap.remove(marriageInfo.getChildUid());
                    }
                }
                applyList.addAll(sexApplyMap.values());
            }
        }
        if(applyList.size() <= 0){
            applyList = refreshServerApplyList(player, childSex);
        }
        return applyList;
    }

    /**
     * 刷新全服联姻列表, 随机获取5个
     */
    public static List<MarriageInfo> refreshServerApplyList(GamePlayer player, int childSex) {
        List<MarriageInfo> applyList = new ArrayList<>();
        if(!userServerApplyMap.containsKey(player.getUserId())) {
            userServerApplyMap.put(player.getUserId(), new ConcurrentHashMap<>());
        }
        Map<Integer, Map<String, MarriageInfo>> userApplyMap = userServerApplyMap.get(player.getUserId());
        if(!userApplyMap.containsKey(childSex)){
            userApplyMap.put(childSex, new ConcurrentHashMap<>());
        }
        Map<String, MarriageInfo> sexApplyMap = userApplyMap.get(childSex);
        List<MarriageInfo> tempList = new ArrayList<>(serverApplyMap.values());
        List<String> npcChildUid = new ArrayList<>();
        for(MarriageInfo marriageInfo : sexApplyMap.values()){
            if(marriageInfo.getUserId() == -1){
                npcChildUid.add(marriageInfo.getChildUid());
            }
        }
        sexApplyMap.clear();
        UserMarriageData userMarriageData = player.getModule(MarriageModule.class).getUserMarriageData();
        long marriageTimes = 0;
        if(null != userMarriageData) {
            marriageTimes = userMarriageData.getTotalMarriageTimes();
        }
        boolean isNpc = false;
        if(marriageTimes <= GameConfig.MARRIAGE_CAN_NPC_TIMES){
            isNpc = true;
        }
        while (true) {
            if (tempList.size() > 0) {
                int randomNum = random.next(0, tempList.size());
                MarriageInfo marriageInfo = tempList.get(randomNum);
                if (marriageInfo.getUserId() != player.getUserId() && marriageInfo.getStatus() == eMarriageStatusType.APPLY &&
                        marriageInfo.getChildSex() != childSex && !isOverApplyTime(marriageInfo)) {
                    if(marriageInfo.getUserId() == -1){
                        if(npcChildUid.contains(marriageInfo.getChildUid())){
                            applyList.add(marriageInfo);
                            sexApplyMap.put(marriageInfo.getChildUid(), marriageInfo);
                        }
                    }else {
                        if(!isNpc) {
                            applyList.add(marriageInfo);
                            sexApplyMap.put(marriageInfo.getChildUid(), marriageInfo);
                        }
                    }
                }
                tempList.remove(randomNum);
            } else {
                break;
            }
            if (applyList.size() >= GameConfig.MARRIAGE_MAX_SEVER_APPLY_LIST_COUNT) {
                break;
            }
        }
        if(isNpc) {
            while (applyList.size() < GameConfig.MARRIAGE_MAX_SEVER_APPLY_LIST_COUNT) {
                MarriageInfo marriageInfo;
                if (childSex == eSexType.MALE.getValue()) {
                    marriageInfo = initNpcMarriage(player, eSexType.FEMALE.getValue());
                } else {
                    marriageInfo = initNpcMarriage(player, eSexType.MALE.getValue());
                }
                applyList.add(marriageInfo);
                sexApplyMap.put(marriageInfo.getChildUid(), marriageInfo);
                serverApplyMap.put(marriageInfo.getChildUid(), marriageInfo);
            }
        }
        return applyList;
    }

    /**
     * 获取未联姻子嗣
     */
    public static List<MarriageInfo> getUnMarryChildList(long userId) {
        List<MarriageInfo> list = new ArrayList<>();
        Map<String, MarriageInfo> childMap = marriageInfoMap.get(userId);
        if (null != childMap) {
            List<MarriageInfo> childList = new ArrayList<>(childMap.values());
            for (MarriageInfo marriageInfo : childList) {
                if (marriageInfo.getStatus() == eMarriageStatusType.WAIT ||
                        marriageInfo.getStatus() == eMarriageStatusType.APPLY ||
                        marriageInfo.getStatus() == eMarriageStatusType.CrossPublish ||
                        marriageInfo.getStatus() == eMarriageStatusType.UnionApply ||
                        marriageInfo.getStatus() == eMarriageStatusType.WaitCrossPublish ||
                        marriageInfo.getStatus() == eMarriageStatusType.WaitCrossWorldPublish ||
                        marriageInfo.getStatus() == eMarriageStatusType.CrossWorldPublish
                    ) {
                    list.add(marriageInfo);
                }
            }
        }
        return list;
    }

    /**
     * 取消联姻
     */
    public static int cancelMarriage(GamePlayer player, String childUid) {
        MarriageInfo child = getAdultChild(player.getUserId(), childUid);
        if (null == child) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if (child.getStatus() == eMarriageStatusType.MARRIED) {
            return GameErrorCode.E_MARRIAGE_ALREADY_MARRY;
        }

        synchronized (locker) {
            if(child.getStatus() == eMarriageStatusType.UnionApply){
                return cancelCrossMarriage(player, childUid, true);
            }else if(child.getStatus() == eMarriageStatusType.CrossWorldPublish){
                return cancelCrossWorldMarriage(player, childUid);
            } else {
                if(child.getStatus() == eMarriageStatusType.APPLY) {
                    if (child.getAppointUserId() != 0) {// 指定联姻
                        if (appointApplyMap.containsKey(child.getAppointUserId())) {
                            appointApplyMap.get(child.getAppointUserId()).remove(childUid);
                        }
                    } else {// 全服联姻
                        serverApplyMap.remove(childUid);
                    }
                    child.setAppointUserId(0);
                    child.setStatus(eMarriageStatusType.WAIT);
                }
                MarriageProto.MarriageApplyCancelRespMsg.Builder resp = MarriageProto.MarriageApplyCancelRespMsg.newBuilder();
                resp.setRet(0);
                player.sendPacket(Protocol.U_MARRIAGE_APPLY_CANCEL, resp);
                return 0;
            }
        }
    }

    public static List<Long> recommendPlayer(GamePlayer player) {//推荐规则，先找在线身份相近的，再找离线身份相近的
        List<Long> recommendList = new ArrayList<>();
        List<Long> onlineUserList = GamePlayerMgr.getAllOnlinePlayerId();
        int titleId = player.getTitleId();
        if(onlineUserList.size() <= GameConfig.MARRIAGE_MAX_RECOMMEND_PLAYER_COUNT + 1){
            for (Long userId : onlineUserList) {
                if(userId == player.getUserId()){//不能是自己
                    continue;
                }
                recommendList.add(userId);
            }
        }else {
            int titleOffset = 0;
            while (recommendList.size() < GameConfig.MARRIAGE_MAX_RECOMMEND_PLAYER_COUNT) {
                for (Long userId : onlineUserList) {
                    if (userId == player.getUserId()) {//不能是自己
                        continue;
                    }
                    UserInfo userInfo = UserMgr.getUserInfo(userId);
                    if (null == userInfo) {
                        continue;
                    }
                    if (Math.abs(titleId - userInfo.getTitleId()) > titleOffset) {
                        continue;
                    }
                    recommendList.add(userId);
                    if (recommendList.size() >= GameConfig.MARRIAGE_MAX_RECOMMEND_PLAYER_COUNT) {
                        break;
                    }
                }
                if (recommendList.size() >= GameConfig.MARRIAGE_MAX_RECOMMEND_PLAYER_COUNT) {
                    break;
                }
                onlineUserList.removeAll(recommendList);
                titleOffset++;
            }
        }
        if(recommendList.size() < GameConfig.MARRIAGE_MAX_RECOMMEND_PLAYER_COUNT){
            List<UserInfo> userInfoList = UserMgr.getUserInfoList();
            int titleOffset = 0;
            while (recommendList.size() < GameConfig.MARRIAGE_MAX_RECOMMEND_PLAYER_COUNT) {
                List<UserInfo> removeList = new ArrayList<>();
                for (UserInfo userInfo : userInfoList) {
                    if (userInfo.getUserId() == player.getUserId()) {//不能是自己
                        removeList.add(userInfo);
                        continue;
                    }
                    if(recommendList.contains(userInfo.getUserId())){//已经在列表中
                        removeList.add(userInfo);
                        continue;
                    }
                    if (Math.abs(titleId - userInfo.getTitleId()) > titleOffset) {
                        continue;
                    }
                    recommendList.add(userInfo.getUserId());
                    if (recommendList.size() >= GameConfig.MARRIAGE_MAX_RECOMMEND_PLAYER_COUNT) {
                        break;
                    }
                }
                if (recommendList.size() >= GameConfig.MARRIAGE_MAX_RECOMMEND_PLAYER_COUNT) {
                    break;
                }
                userInfoList.removeAll(removeList);
                titleOffset++;
                if(titleOffset > TitleMgr.getMaxTitleId()){
                    break;
                }
            }
        }
        return recommendList;
    }

    /**
     * 检测超时的发布
     */
    public static void checkApplyOverTime() {
        for(Map<String, MarriageInfo> childMap : marriageInfoMap.values()){
            List<MarriageInfo> childList = new ArrayList<>(childMap.values());
            for (MarriageInfo marriageInfo : childList) {
                if (marriageInfo.getStatus() == eMarriageStatusType.WaitCrossWorldPublish){
                    //跨服未响应等异常情况
                    if (System.currentTimeMillis()/1000 - marriageInfo.getPublishTime() > DateHelper.HOUR_SECONDS) {
                        synchronized (locker) {
                            marriageInfo.setStatus(eMarriageStatusType.WAIT);
                        }
                        continue;
                    }
                }
                if (marriageInfo.getStatus() == eMarriageStatusType.APPLY || marriageInfo.getStatus() == eMarriageStatusType.CrossWorldPublish) {
                    if (isOverApplyTime(marriageInfo)) {
                        synchronized (locker) {
                            if (marriageInfo.getStatus() == eMarriageStatusType.APPLY || marriageInfo.getStatus() == eMarriageStatusType.CrossWorldPublish) {
                                marriageInfo.setStatus(eMarriageStatusType.WAIT);
                            }
                        }
                        serverApplyMap.remove(marriageInfo.getChildUid());
                        Map<String, MarriageInfo> adultChildMap = appointApplyMap.get(marriageInfo.getAppointUserId());
                        if (null != adultChildMap) {
                            adultChildMap.remove(marriageInfo.getChildUid());
                        }
                    }
                }
            }
        }
    }

    private static UserMarriageData getUserMarriageData(long userId){
        return userMarriageDataMap.get(userId);
    }

    /**
     * 增加联姻记录
     */
    public static MarriageRecord addMarriageRecord(MarriageInfo myChild, MarriageInfo partnerChild, BigInteger addChildEarnSpeed) {
        MarriageRecord marriageRecord = new MarriageRecord();
        marriageRecord.setUserId(myChild.getUserId());
        marriageRecord.setUserNickName(myChild.getUserNickName());
        marriageRecord.setChildUid(myChild.getChildUid());
        marriageRecord.setChildTalent(myChild.getChildTalent());
        marriageRecord.setChildSex(myChild.getChildSex());
        marriageRecord.setChildOccupation(myChild.getChildOccupation());
        marriageRecord.setChildName(myChild.getChildName());
        marriageRecord.setChildHeadIcon(myChild.getChildHeadIcon());
        marriageRecord.setChildAdultTime(myChild.getChildAdultTime());
        marriageRecord.setPublishTime(myChild.getPublishTime());
        marriageRecord.setAppointUserId(myChild.getAppointUserId());
        marriageRecord.setReward(myChild.getReward());
        marriageRecord.setMarriageTime(System.currentTimeMillis() / 1000);
        marriageRecord.setChildOccupationId(myChild.getChildOccupationId());
        marriageRecord.setChildEarnSpeed(myChild.getChildEarnSpeed());
        marriageRecord.setMotherId(myChild.getMotherBeautyId());
        marriageRecord.setPartnerUserId(partnerChild.getUserId());
        marriageRecord.setPartnerNickName(partnerChild.getUserNickName());
        marriageRecord.setPartnerChildUid(partnerChild.getChildUid());
        marriageRecord.setPartnerChildTalent(partnerChild.getChildTalent());
        marriageRecord.setPartnerChildOccupation(partnerChild.getChildOccupation());
        marriageRecord.setPartnerChildName(partnerChild.getChildName());
        marriageRecord.setPartnerChildSex(partnerChild.getChildSex());
        marriageRecord.setPartnerChildHeadIcon(partnerChild.getChildHeadIcon());
        marriageRecord.setPartnerReward(partnerChild.getReward());
        marriageRecord.setPartnerChildOccupationId(partnerChild.getChildOccupationId());
        marriageRecord.setPartnerChildEarnSpeed(partnerChild.getChildEarnSpeed());
        marriageRecord.setPartnerMotherId(partnerChild.getMotherBeautyId());
        marriageRecord.setTotalChildEarnSpeed(marriageRecord.getChildEarnSpeed().add(addChildEarnSpeed));
        marriageRecord.setTwins(myChild.isTwins());
        marriageRecord.setPartnerTwins(partnerChild.isTwins());
        marriageRecord.setServerId(myChild.getServerId());
        marriageRecord.setPartnerServerId(partnerChild.getServerId());
        marriageRecord.setInsertOption();

        GamePlayer player = GamePlayerMgr.getOnlinePlayer(myChild.getUserId());
        if(null != player){
            player.getModule(MarriageModule.class).addMarriageRecord(marriageRecord);
        }else {
            MarriageBusiness.addMarriageRecord(marriageRecord);
        }
        return marriageRecord;
    }

    /**
     * 联姻成功
     * @param player  当前玩家
     * @param myChild 玩家子嗣联系信息
     * @param partnerChild 对方子嗣联姻信息
     * @param isCrossMarriage 是否跨服联姻
     * @param isUnionMarriage 是否商会联姻
     */
    private static MarriageResult marrySuccess(GamePlayer player, MarriageInfo myChild, MarriageInfo partnerChild, boolean isCrossMarriage,
                                               boolean isUnionMarriage) {
        MarriageResult result = new MarriageResult();
        synchronized (locker) {
            if(isCrossMarriage) {//跨服联姻只处理自己
                if (myChild.getStatus() == eMarriageStatusType.MARRIED) {
                    result.setRet(GameErrorCode.E_MARRIAGE_ALREADY_MARRY);
                    return result;
                }
            }else {//本服联姻需要处理对方数据
                if(myChild.getStatus() == eMarriageStatusType.CrossPublish){
                    result.setRet(GameErrorCode.E_CROSS_MARRIAGE_STATUS_CROSS_PUBLISH);
                    return result;
                }else if (myChild.getStatus() == eMarriageStatusType.MARRIED) {
                    result.setRet(GameErrorCode.E_MARRIAGE_ALREADY_MARRY);
                    return result;
                }
                if (partnerChild.getStatus() != eMarriageStatusType.APPLY && partnerChild.getStatus() != eMarriageStatusType.CrossWorldPublish){
                    result.setRet(GameErrorCode.E_MARRIAGE_STATE_NO_APPLY);
                    return result;
                }
                partnerChild.setStatus(eMarriageStatusType.MARRIED);
            }
            myChild.setStatus(eMarriageStatusType.MARRIED);
        }
        UserInfo myUserInfo = player.getUserInfo();
        BigInteger myAddEarnSpeed;
        //双方发放道具奖励
        myAddEarnSpeed = mailSendMarriageReward(myChild, partnerChild, myUserInfo, isCrossMarriage);
        //联姻成功，增加记录
        MarriageRecord marriageRecord = addMarriageRecord(myChild, partnerChild, myAddEarnSpeed);
        //累计联姻次数增加
        UserMarriageData userData = getUserMarriageData(myChild.getUserId());
        if(null != userData) {
            userData.setTotalMarriageTimes(userData.getTotalMarriageTimes() + 1);
        }
        player.getModule(MarriageModule.class).syncUserMarriageData();

        //成就、任务
        player.notifyListener(eGamePlayerEventType.MarriageTimes.getValue(), 1);
        player.notifyListener(eGamePlayerEventType.UnionMarriageTimes.getValue(), new UnionActivityTaskArgs(UnionActivityTaskArgs.TYPE_ADD_VALUE, 1, player.getUserInfo().getUnionUid()));
        player.notifyListener(eGamePlayerEventType.TotalChildMarriageTime.getValue(),1);
        player.notifyListener(eGamePlayerEventType.MarriageTotalTimes.getValue(), userData);
        player.notifyListener(eGamePlayerEventType.ChildEarnSpeedRiseRank.getValue(), myAddEarnSpeed);
        //小面任务
        player.getModule(XiaoMianModule.class).addMarriageTimes(1);
        player.getModule(EarnSpeedModule.class).scheduleAddSilver();
        if(!isCrossMarriage) {
            if (partnerChild.getUserId() > 0) {//不是NPC
                threadTaskManger.addTask(0, new PartnerMarrySuccessTask(partnerChild, myChild, false));
                //亲家系统
                RelativesMgr.syncMarriageSuccess(userData.getUserId(), partnerChild.getUserId());
            }
        }else {
            //亲家系统（跨服）
            RelativesMgr.syncCrossMarriageSuccess(userData.getUserId(), partnerChild.getUserId());
        }
        result.setRet(0);
        result.setMarriageRecord(marriageRecord);
        DataAnalyticsMgr.trackMarriageMarrySuccess(myChild,partnerChild,isCrossMarriage,isUnionMarriage);

        return result;
    }

    public static BigInteger mailSendMarriageReward(MarriageInfo myChild, MarriageInfo partnerChild, UserInfo myUserInfo, boolean isCross) {
        BigInteger myAddEarnSpeed;
        String mailTitle;
        String mailContent;
        if(isCross){
            mailTitle = MultipleLanguageMgr.getContent(MailManager.CROSS_MARRIAGE_SUCCESS_TITLE, myUserInfo.getLanguage());
            ServerInfo partnerServer = ServerListMgr.getServerInfo(partnerChild.getServerId());
            mailContent = StringUtils.format(MultipleLanguageMgr.getContent(MailManager.CROSS_MARRIAGE_SUCCESS_CONTENT, myUserInfo.getLanguage()),
                    myChild.getChildName(), partnerServer.getServerName(), partnerChild.getUserNickName(), partnerChild.getChildName());
        }else {
            mailTitle = MultipleLanguageMgr.getContent(MailManager.MARRIAGE_SUCCESS_TITLE, myUserInfo.getLanguage());
            mailContent = StringUtils.format(MultipleLanguageMgr.getContent(MailManager.MARRIAGE_SUCCESS_CONTENT, myUserInfo.getLanguage()),
                    myChild.getChildName(), partnerChild.getUserNickName(), partnerChild.getChildName());
        }
        MailManager.sendMail(myUserInfo.getUserId(), eMailType.DAILY.getValue(), PropertyHelper.parsePropertyToString(partnerChild.getReward()), mailContent, mailTitle);

        // 日志：联姻成功奖励
        AutoLogMgr.add(new LogMarriageReward(myUserInfo.getUserId(), myChild.getChildUid(), PropertyHelper.parsePropertyToString(partnerChild.getReward())));

        myAddEarnSpeed = new BigDecimal(myChild.getChildEarnSpeed()).multiply(BigDecimal.valueOf(GameConfig.MARRIAGE_ADD_EARN_SPEED_MAX_PARAM)).
                divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_UP).toBigInteger();
        if(partnerChild.getChildEarnSpeed().compareTo(myAddEarnSpeed) < 0){
            myAddEarnSpeed = partnerChild.getChildEarnSpeed();
        }
        //增加日志
        BigInteger oldEarnSpeed = myUserInfo.getChildEarnSpeed();
        myUserInfo.setChildEarnSpeed(myUserInfo.getChildEarnSpeed().add(myAddEarnSpeed));
        LogMgr.addLogChildAddEarnSpeed(myUserInfo.getUserId(), myChild.getChildUid(), 1, oldEarnSpeed, myAddEarnSpeed, myUserInfo.getEarnSpeed());
        return myAddEarnSpeed;
    }

    /**
     * 处理本服联姻
     * @param player   玩家
     * @param myChild  玩家子嗣联姻信息
     * @param partnerChild 对方
     * @return
     */
    private static MarriageResult dealMarriage(GamePlayer player, MarriageInfo myChild, MarriageInfo partnerChild) {
        MarriageResult result = new MarriageResult();
        int ret = canMarriage(myChild, partnerChild);
        if (ret != 0) {
            result.setRet(ret);
        } else {//联姻成功
            result = marrySuccess(player, myChild, partnerChild, false, false);
        }
        return result;
    }

    private static int canMarriage(MarriageInfo myChild, MarriageInfo partnerChild) {
        if (null == myChild) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if (myChild.getStatus() == eMarriageStatusType.CrossPublish){
            return GameErrorCode.E_CROSS_MARRIAGE_STATUS_CROSS_PUBLISH;
        }
        if (myChild.getStatus() == eMarriageStatusType.UnionApply){
            return GameErrorCode.E_CROSS_MARRIAGE_STATUS_CROSS_UNION_PUBLISH;
        }

        if (null == partnerChild) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }
        if(myChild.getUserId() == partnerChild.getUserId()){
            return GameErrorCode.E_MARRIAGE_NO_SELF;
        }
        //检查双方性别 等等
        if (myChild.getChildSex() == partnerChild.getChildSex()) {
            return GameErrorCode.E_MARRIAGE_SEX_WRONG;
        }
        //判断是否过期
        if(isOverApplyTime(partnerChild)){
            return GameErrorCode.E_MARRIAGE_IS_OVER_TIME;
        }

        if (partnerChild.getStatus() != eMarriageStatusType.APPLY && partnerChild.getStatus() != eMarriageStatusType.CrossWorldPublish) {
            return GameErrorCode.E_MARRIAGE_STATE_NO_APPLY;
        }
        //已经联姻
        if (myChild.getStatus() == eMarriageStatusType.MARRIED || partnerChild.getStatus() == eMarriageStatusType.MARRIED) {
            return GameErrorCode.E_MARRIAGE_ALREADY_MARRY;
        }

        return 0;
    }

    /**
     * 发布时间是否超时
     */
    private static boolean isOverApplyTime(MarriageInfo marriageInfo) {
        return System.currentTimeMillis()/1000 - marriageInfo.getPublishTime() > GameConfig.MARRIAGE_PUBLISH_TIME;
    }

    /**-
     * 获取玩家等待发布列表
     */
    private static List<MarriageInfo> getWaitPublishList(long userId){
        List<MarriageInfo> waitList = new ArrayList<>();
        Map<String, MarriageInfo> dataMap = marriageInfoMap.get(userId);
        if(null != dataMap){
            for(MarriageInfo marriageInfo : dataMap.values()){
                if(marriageInfo.getStatus() == eMarriageStatusType.WAIT){
                    waitList.add(marriageInfo);
                }
            }
        }
        return waitList;
    }

    public static MarriageInfo getAdultChild(long userId, String myChildUid) {
        if(marriageInfoMap.containsKey(userId)) {
            return marriageInfoMap.get(userId).get(myChildUid);
        }
        return null;
    }

    public static int getUnMarryChildCount(long userId) {
        List<MarriageInfo> list = getUnMarryChildList(userId);
        return list.size();
    }

    //开完宴会，状元宴要给子嗣加赚速
    public static String childBanquetAddEarnSpeed(GamePlayer player,String childUid,int addRatio){
        MarriageInfo marriageInfo = getAdultChild(player.getUserId(),childUid);
        if (marriageInfo == null) {
            marriageInfo =  MarriageBusiness.getMarriageInfoByChildId(GameServer.getInstance().getServerId(),player.getUserId(),childUid);
        }
        if(marriageInfo != null){
            BigInteger originEarnSpeed = marriageInfo.getChildEarnSpeed();
            BigInteger addValue = originEarnSpeed.multiply(BigInteger.valueOf(addRatio)).divide(BigInteger.valueOf(1000));
            UserInfo userInfo = player.getUserInfo();
            //增加日志
            BigInteger oldEarnSpeed = userInfo.getChildEarnSpeed();
            userInfo.setChildEarnSpeed(userInfo.getChildEarnSpeed().add(addValue));
            LogMgr.addLogChildAddEarnSpeed(userInfo.getUserId(), childUid, 2, oldEarnSpeed, addValue, userInfo.getChildEarnSpeed());
            return addValue.toString();
        }
        return "";
    }

    /**
     * 发布跨服联姻
     * @param player
     * @param childUid
     * @param limitEarnSpeed
     * @param appointUserId
     * @param partnerServerId
     * @return
     */
    public static int publishCrossMarriageApply(GamePlayer player, String childUid, BigInteger limitEarnSpeed, long appointUserId, long partnerServerId) {
        MarriageInfo marriageInfo = getAdultChild(player.getUserId(), childUid);
        if (null == marriageInfo) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if(limitEarnSpeed.compareTo(BigInteger.ZERO) > 0){//有设置最低赚速
            if(MarriageConfigMgr.isOverMarriageLimitEarnSpeedConfig(marriageInfo.getChildEarnSpeed(), limitEarnSpeed)){
                return GameErrorCode.E_CROSS_MARRIAGE_LIMIT_EARNSPEED_OVER_CONFIG;
            }
        }

        if(appointUserId > 0 && !ServerListMgr.isServerProvideServices(partnerServerId)){
            return GameErrorCode.E_CROSS_MARRIAGE_PARTNER_SERVER_MAINTAIN;
        }

        // 相同商会跨服判断
        long serverId = ConfigHelper.getLong("serverId");
        int groupId = CrossUnionServerGroupMgr.getGroupIdByServerId(serverId);
        boolean isSameCross = false;
        // 开启商会跨服且处于相同跨服的指定联姻才做判断
        if(appointUserId > 0) {
            int partnerGroupId = CrossUnionServerGroupMgr.getGroupIdByServerId(partnerServerId);
            isSameCross = partnerGroupId == groupId;
        }

        synchronized (locker) {
            if (marriageInfo.getStatus() != eMarriageStatusType.WAIT) {// 已经申请 或者 已经完成
                return GameErrorCode.E_MARRIAGE_STATE_NO_WAIT;
            }
            // 不属于同一个商会跨服才做物品消耗
            if(!isSameCross){
                Property consume = ConfigMgr.getMarriageConsume(marriageInfo.getChildEarnSpeed());
                if(null == consume){
                    return GameErrorCode.E_CROSS_MARRIAGE_CONSUME_CONFIG_ERROR;
                }
                if(!player.getModule(CurrencyModule.class).currencyIsEnough(consume)){
                    return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
                }
                //预扣除
                player.getModule(PreReduceModule.class).preReduce(PropertyHelper.parsePropertyToString(consume));
            }
            marriageInfo.setStatus(eMarriageStatusType.WaitCrossPublish);
            marriageInfo.setPublishTime(System.currentTimeMillis() / 1000);
        }
        //发送到跨服
        CrossMarriageProto.CrossMarriageApplyPublishCrossReqMsg.Builder pushMsg = CrossMarriageProto.CrossMarriageApplyPublishCrossReqMsg.newBuilder();
        pushMsg.setApplyData(MarriagePb.parseMarriageApplyTempMsg(marriageInfo));
        pushMsg.setLimitEarnSpeed(limitEarnSpeed.toString());
        pushMsg.setAppointUserId(appointUserId);
        pushMsg.setPartnerServerId(partnerServerId);
        YanQuMessage pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_PUBLISH_MARRIAGE, pushMsg);
        GamePlayerMgr.sendPacket(player.getUserId(), pbMsg);
        return 0;
    }

    /**
     * 跨服联姻发布后响应
     * @param player
     * @param reqMsg
     */
    public static int crossMarriagePublishResponse(GamePlayer player, CrossMarriageProto.CrossMarriageApplyPublishCrossRespMsg reqMsg){
        MarriageInfo marriageInfo = getAdultChild(player.getUserId(), reqMsg.getChildUid());
        if (null == marriageInfo) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        Property consume = null;
        String marriageConsume = null;

        // 相同商会跨服判断
        long partnerServerId = reqMsg.getPartnerServerId();
        long appointUserId = reqMsg.getAppointUserId();
        long serverId = ConfigHelper.getLong("serverId");
        int groupId = CrossUnionServerGroupMgr.getGroupIdByServerId(serverId);
        boolean isSameCross = false;
        // 开启商会跨服且处于相同跨服的指定联姻才做判断
        if(appointUserId > 0) {
            int partnerGroupId = CrossUnionServerGroupMgr.getGroupIdByServerId(partnerServerId);
            isSameCross = partnerGroupId == groupId;
        }
        // 不属于同一个商会跨服才做物品消耗，屏蔽恢复预扣除
        if (!isSameCross) {
            consume = ConfigMgr.getMarriageConsume(marriageInfo.getChildEarnSpeed());
            marriageConsume = PropertyHelper.parsePropertyToString(consume);
            //恢复预扣除
            player.getModule(PreReduceModule.class).restorePreReduce(marriageConsume);
        }

        synchronized (locker) {
            if (marriageInfo.getStatus() != eMarriageStatusType.WaitCrossPublish) {//状态不对
                return GameErrorCode.E_CROSS_MARRIAGE_STATE_HAS_CHANGE;
            }
            if(reqMsg.getRet() == 0) {
                if(!player.getModule(CurrencyModule.class).removeCurrency(consume, eLogMoneyType.Marriage, eLogMoneyType.CrossMarriageConsume)){
                    return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
                }
                marriageInfo.setMarriageConsume(marriageConsume);
                marriageInfo.setPublishTime(reqMsg.getPublishTime());
                marriageInfo.setLimitEarnSpeed(new BigInteger(reqMsg.getLimitEarnSpeed()));
                marriageInfo.setAppointUserId(reqMsg.getAppointUserId());
                marriageInfo.setPartnerServerId(reqMsg.getPartnerServerId());
                marriageInfo.setStatus(eMarriageStatusType.CrossPublish);
                player.getModule(MarriageModule.class).syncUnMarriageChild(marriageInfo);
                //增加发布日志
                AutoLogMgr.add(new LogPublishMarriageApply(player.getUserId(), marriageInfo.getChildUid(), marriageInfo.getChildName(), true,
                        marriageInfo.getLimitEarnSpeed()));
            }else {
                marriageInfo.setStatus(eMarriageStatusType.WAIT);
            }
        }
        return reqMsg.getRet();
    }

    /**
     * 取消跨服联姻
     */
    public static int cancelCrossMarriage(GamePlayer player, String childUid, boolean isUnion) {
        MarriageInfo child = getAdultChild(player.getUserId(), childUid);
        if (null == child) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if (child.getStatus() == eMarriageStatusType.MARRIED) {
            return GameErrorCode.E_MARRIAGE_ALREADY_MARRY;
        }

        //发送到跨服取消联姻
        MarriageProto.MarriageApplyCancelReqMsg.Builder reqMsg = MarriageProto.MarriageApplyCancelReqMsg.newBuilder();
        reqMsg.setChildUid(childUid);
        YanQuMessage pbMsg;
        if(isUnion){
            pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_CANCEL_UNION_MARRIAGE, reqMsg);
        }else {
            pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_CANCEL_MARRIAGE, reqMsg);
        }
        GamePlayerMgr.sendPacket(player.getUserId(), pbMsg);
        return 0;
    }

    /**
     * 取消跨服联姻
     */
    public static int crossMarriageCancelResponse(long userId, String childUid) {
        MarriageInfo child = getAdultChild(userId, childUid);
        if (null == child) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if (child.getStatus() == eMarriageStatusType.MARRIED) {
            return GameErrorCode.E_MARRIAGE_ALREADY_MARRY;
        }

        synchronized (locker) {
            if(child.getStatus() == eMarriageStatusType.CrossPublish) {
                child.setAppointUserId(0);
                child.setPartnerServerId(0);
                child.setStatus(eMarriageStatusType.WAIT);
            }
            //返回消耗邮件
            sendConsumeMail(userId, child);
        }
        return 0;
    }

    /**
     * 发送消费邮件
     * @param userId
     * @param marriageInfo
     */
    private static void sendConsumeMail(long userId, MarriageInfo marriageInfo){
        if (StringUtils.isNullOrEmpty(marriageInfo.getMarriageConsume())){
            return;
        }
        String language = UserMgr.getLanguage(userId);
        String mailTitle = MultipleLanguageMgr.getContent(MailManager.CROSS_MARRIAGE_CONSUME_TITLE, language);
        String mailContent = MultipleLanguageMgr.getContent(MailManager.CROSS_MARRIAGE_CONSUME_CONTENT, language);
        MailManager.sendMail(userId, eMailType.DAILY.getValue(), marriageInfo.getMarriageConsume(), mailContent, mailTitle);
        marriageInfo.setMarriageConsume("");
    }

    /**
     * 取消本服联姻（跨世界）
     */
    public static int cancelCrossWorldMarriage(GamePlayer player, String childUid){
        MarriageInfo child = getAdultChild(player.getUserId(), childUid);
        if (null == child) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if (child.getStatus() == eMarriageStatusType.MARRIED) {
            return GameErrorCode.E_MARRIAGE_ALREADY_MARRY;
        }

        //发送到跨服取消联姻
        MarriageProto.MarriageApplyCancelReqMsg.Builder reqMsg = MarriageProto.MarriageApplyCancelReqMsg.newBuilder();
        reqMsg.setChildUid(childUid);
        YanQuMessage pbMsg;
        pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_CROSS_WORLD_CANCEL_MARRIAGE, reqMsg);
        GamePlayerMgr.sendPacket(player.getUserId(), pbMsg);
        return 0;

    }

    /**
     * 取消本服联姻(返回)（跨世界）
     */
    public static int crossWorldMarriageCancelResponse(long userId, String childUid) {
        MarriageInfo child = getAdultChild(userId, childUid);
        if (null == child) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        synchronized (locker) {
            if (child.getStatus() == eMarriageStatusType.MARRIED) {
                return GameErrorCode.E_MARRIAGE_ALREADY_MARRY;
            }
            if(child.getStatus() == eMarriageStatusType.CrossWorldPublish) {
                child.setAppointUserId(0);
                child.setPartnerServerId(0);
                child.setStatus(eMarriageStatusType.WAIT);
                serverApplyMap.remove(childUid);
            }
        }
        return 0;
    }

    /**
     * 跨服来到联姻对方处理数据 （跨世界）
     * @param marriageInfo   发起同意联姻的玩家
     * @param partnerUserId  本服玩家（发起联姻申请）
     * @param partnerChildUid  本服玩家的子嗣
     * @return
     */
    public static CrossMarriageResult toCrossWorldPartnerDealAgree(MarriageInfo marriageInfo, long partnerUserId, String partnerChildUid){
        CrossMarriageResult marriageResult = new CrossMarriageResult();
        MarriageInfo partnerMarriage = getAdultChild(partnerUserId, partnerChildUid);
        if(null == partnerMarriage){
            marriageResult.setRet(GameErrorCode.E_MARRIAGE_APPLY_NO_FOUND);
            return marriageResult;
        }
        synchronized (locker) {
            if(partnerMarriage.getStatus() != eMarriageStatusType.CrossWorldPublish){
                marriageResult.setRet(GameErrorCode.E_CROSS_MARRIAGE_STATE_HAS_CHANGE);
                return marriageResult;
            }
            partnerMarriage.setStatus(eMarriageStatusType.MARRIED);
        }
        threadTaskManger.addTask(0, new AddCrossMarryTimesTask(partnerUserId, marriageInfo.getUserId(), marriageInfo.getServerId()));
        threadTaskManger.addTask(0, new PartnerMarrySuccessTask(partnerMarriage, marriageInfo, true));
        marriageResult.setMarriageInfo(partnerMarriage);
        return marriageResult;
    }

    /**
     * 跨服同意联姻响应 （跨世界）
     * @param dealResult
     * @param marriageInfo
     * @param partnerMarriage
     * @return
     */
    public static MarriageResult agreeCrossWorldMarriageResponse(int dealResult, MarriageInfo marriageInfo, MarriageInfo partnerMarriage) {
        MarriageResult marriageResult = new MarriageResult();
        marriageResult.setRet(dealResult);
        if(0 != dealResult){
            return marriageResult;
        }
        GamePlayer player = GamePlayerMgr.getPlayer(marriageInfo.getUserId());
        if(null != player) {
            MarriageInfo myChild = getAdultChild(marriageInfo.getUserId(), marriageInfo.getChildUid());
            if(null != myChild) {
                //Property consume = ConfigMgr.getMarriageConsume(myChild.getChildEarnSpeed());
                //player.getModule(CurrencyModule.class).removeCurrency(consume, eLogMoneyType.Marriage, eLogMoneyType.CrossMarriageConsume);
                marriageResult = marrySuccess(player, myChild, partnerMarriage, true, false);
            }
        }
        return marriageResult;
    }

    /**
     * 通知被拒绝
     * @param userId
     * @param childList
     */
    public static void refuseCrossMarriage(long userId, List<String> childList) {
        Map<String, MarriageInfo> userMarriageMap = marriageInfoMap.get(userId);
        if(null != userMarriageMap){
            List<String> syncList = new ArrayList<>();
            for(String childUid : childList){
                MarriageInfo marriageInfo = userMarriageMap.get(childUid);
                if(null != marriageInfo) {
                    synchronized (locker) {
                        if(marriageInfo.getStatus() == eMarriageStatusType.CrossPublish){
                            marriageInfo.setAppointUserId(0);
                            marriageInfo.setPartnerServerId(0);
                            marriageInfo.setStatus(eMarriageStatusType.WAIT);
                            sendConsumeMail(userId, marriageInfo);
                            syncList.add(childUid);
                        }
                    }
                }
            }
            GamePlayer player = GamePlayerMgr.getOnlinePlayer(userId);
            if(null != player){//通知拒绝
                player.getModule(MarriageModule.class).crossMarriageRefuseNotify(syncList);
            }
        }
    }

    /**
     * 同意跨服联姻
     */
    public static int agreeCrossMarriage(GamePlayer player, String myChildUid, String partnerChildUid) {
        MarriageInfo child = getAdultChild(player.getUserId(), myChildUid);
        if (null == child) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if (child.getStatus() == eMarriageStatusType.MARRIED) {
            return GameErrorCode.E_MARRIAGE_ALREADY_MARRY;
        }
        if (child.getStatus() == eMarriageStatusType.CrossPublish){
            return GameErrorCode.E_CROSS_MARRIAGE_STATUS_CROSS_PUBLISH;
        }
        if (child.getStatus() == eMarriageStatusType.UnionApply){
            return GameErrorCode.E_CROSS_MARRIAGE_STATUS_CROSS_UNION_PUBLISH;
        }
        if(!player.getModule(CurrencyModule.class).currencyIsEnough(PropertyHelper.parseStringToProperty(GameConfig.CROSS_MARRIAGE_CONSUME_CONFIG))){
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }

        CrossMarriageProto.CrossMarriageAgreeCrossDealReqMsg.Builder dealMsg = CrossMarriageProto.CrossMarriageAgreeCrossDealReqMsg.newBuilder();
        dealMsg.setApplyData(MarriagePb.parseMarriageApplyTempMsg(child));
        dealMsg.setPartnerChildUid(partnerChildUid);
        YanQuMessage pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_AGREE_MARRIAGE_DEAL, dealMsg);
        GamePlayerMgr.sendPacket(player.getUserId(), pbMsg);
        return 0;
    }

    /**
     * 跨服同意联姻响应
     * @param dealResult
     * @param marriageInfo
     * @param partnerMarriage
     * @return
     */
    public static MarriageResult agreeCrossMarriageResponse(int dealResult, MarriageInfo marriageInfo, MarriageInfo partnerMarriage) {
        MarriageResult marriageResult = new MarriageResult();
        marriageResult.setRet(dealResult);
        if(0 != dealResult){
            return marriageResult;
        }
        long userId = marriageInfo.getUserId();
        GamePlayer player = GamePlayerMgr.getPlayer(userId);
        if(null != player) {
            MarriageInfo myChild = getAdultChild(marriageInfo.getUserId(), marriageInfo.getChildUid());
            if(null != myChild) {
                // 判断如果对方发起的是相同商会跨服指定联姻，则不消耗材料
                long appointUserId = partnerMarriage.getAppointUserId();
                int groupId = CrossUnionServerGroupMgr.getGroupIdByServerId(GameServer.getInstance().getServerId());
                int partnerGroupId = CrossUnionServerGroupMgr.getGroupIdByServerId(partnerMarriage.getServerId());
                // 开启商会跨服且处于相同跨服的指定联姻
                if (groupId != partnerGroupId || appointUserId != userId) {
                    Property consume = ConfigMgr.getMarriageConsume(myChild.getChildEarnSpeed());
                    player.getModule(CurrencyModule.class).removeCurrency(consume, eLogMoneyType.Marriage, eLogMoneyType.CrossMarriageConsume);
                }
                marriageResult = marrySuccess(player, myChild, partnerMarriage, true, false);
            }
        }
        return marriageResult;
    }

    /**
     * 跨服来到联姻对方处理数据
     * @param marriageInfo   发起同意联姻的玩家
     * @param partnerUserId  本服玩家（发起联姻申请）
     * @param partnerChildUid  本服玩家的子嗣
     * @return
     */
    public static CrossMarriageResult toPartnerDealAgree(MarriageInfo marriageInfo, long partnerUserId, String partnerChildUid){
        CrossMarriageResult marriageResult = new CrossMarriageResult();
        MarriageInfo partnerMarriage = getAdultChild(partnerUserId, partnerChildUid);
        if(null == partnerMarriage){
            marriageResult.setRet(GameErrorCode.E_MARRIAGE_APPLY_NO_FOUND);
            return marriageResult;
        }
        synchronized (locker) {
            if(partnerMarriage.getStatus() != eMarriageStatusType.CrossPublish){
                marriageResult.setRet(GameErrorCode.E_CROSS_MARRIAGE_STATE_HAS_CHANGE);
                return marriageResult;
            }
            partnerMarriage.setStatus(eMarriageStatusType.MARRIED);
        }
        threadTaskManger.addTask(0, new AddCrossMarryTimesTask(partnerUserId, marriageInfo.getUserId(), marriageInfo.getServerId()));
        threadTaskManger.addTask(0, new PartnerMarrySuccessTask(partnerMarriage, marriageInfo, true));
        marriageResult.setMarriageInfo(partnerMarriage);
        return marriageResult;
    }

    public static int checkCrossAppointMarriage(long userId, long serverId, long appointUserId) {
        UserMarriageData appointData = getUserMarriageData(appointUserId);
        if(null == appointData){
            return GameErrorCode.E_MARRIAGE_PARTNER_NO_OPEN;
        }
        if(!appointData.isAcceptCrossAppoint()){
            return GameErrorCode.E_MARRIAGE_NO_ACCEPT_APPOINT;
        }
        if(BlacklistMgr.isInCrossBlacklist(appointUserId, eBlacklistType.Marriage.getValue(), userId, serverId)){
            return GameErrorCode.E_IN_PLAYER_BLACKLIST;
        }
        return 0;
    }

    /**
     * 跨服联姻超时检测
     */
    public static void checkCrossApplyOverTime() {
        for(Map<String, MarriageInfo> childMap : marriageInfoMap.values()){
            List<MarriageInfo> childList = new ArrayList<>(childMap.values());
            for (MarriageInfo marriageInfo : childList) {
                if (marriageInfo.getStatus() == eMarriageStatusType.CrossPublish) {
                    if (isOverApplyTime(marriageInfo)) {
                        synchronized (locker) {
                            if(marriageInfo.getStatus() == eMarriageStatusType.CrossPublish) {
                                marriageInfo.setStatus(eMarriageStatusType.WAIT);
                            }
                            sendConsumeMail(marriageInfo.getUserId(), marriageInfo);
                        }
                        //通知跨服联姻超时
                        notifyCrossMarriageOverTime(marriageInfo, false);
                    }
                }else if(marriageInfo.getStatus() == eMarriageStatusType.UnionApply){
                    if (isOverApplyTime(marriageInfo)) {
                        synchronized (locker) {
                            if(marriageInfo.getStatus() == eMarriageStatusType.UnionApply) {
                                marriageInfo.setStatus(eMarriageStatusType.WAIT);
                            }
                        }
                        //通知跨服商会联姻超时
                        notifyCrossMarriageOverTime(marriageInfo, true);
                    }
                }else if(marriageInfo.getStatus() == eMarriageStatusType.WaitCrossPublish){
                    if(isWaitCrossStatusOverTime(marriageInfo)){
                        synchronized (locker) {
                            if(marriageInfo.getStatus() == eMarriageStatusType.WaitCrossPublish) {
                                marriageInfo.setStatus(eMarriageStatusType.WAIT);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 通知跨服联姻超时
     * @param marriageInfo
     */
    private static void notifyCrossMarriageOverTime(MarriageInfo marriageInfo, boolean isUnion){
        CrossMarriageProto.CrossMarriageOverTimeNotifyMsg.Builder notifyMsg = CrossMarriageProto.CrossMarriageOverTimeNotifyMsg.newBuilder();
        notifyMsg.setChildUid(marriageInfo.getChildUid());
        YanQuMessage pbMsg;
        if(isUnion){
            pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_UNION_MARRIAGE_OVER_TIME_NOTIFY, notifyMsg);
        }else {
            pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_MARRIAGE_OVER_TIME_NOTIFY, notifyMsg);
        }
        GamePlayerMgr.sendPacket(marriageInfo.getUserId(), pbMsg);
    }

    /**
     * 是否超过跨服响应时间，预定义10s
     */
    private static boolean isWaitCrossStatusOverTime(MarriageInfo marriageInfo) {
        return System.currentTimeMillis()/1000 - marriageInfo.getPublishTime() > 10;
    }

    /**
     * 分组变更返回消耗道具
     */
    private static void groupChange(){
        if(!CrossMarriageGroupMgr.canCrossMarriage(GameServer.getInstance().getServerId())) {
            synchronized (locker) {
                for (Map<String, MarriageInfo> childMap : marriageInfoMap.values()) {
                    List<MarriageInfo> childList = new ArrayList<>(childMap.values());
                    for (MarriageInfo marriageInfo : childList) {
                        if (marriageInfo.getStatus() == eMarriageStatusType.CrossPublish) {
                            marriageInfo.setStatus(eMarriageStatusType.WAIT);
                            sendConsumeMail(marriageInfo.getUserId(), marriageInfo);
                        }
                    }
                }
            }
        }
    }

    /**
     * 获取商会联姻列表
     */
    public static void getUnionApplyList(GamePlayer player, int childSex) {
        String unionUid = player.getUserInfo().getUnionUid();
        if(StringUtils.isNullOrEmpty(unionUid)){
            MarriageProto.MarriageApplyListMsg.Builder respMsg = MarriagePb.parseMarriageApplyListMsg(new ArrayList<>());
            player.sendPacket(Protocol.U_MARRIAGE_GET_SERVER_APPLY_LIST, respMsg);
            return;
        }
        CrossMarriageProto.CrossMarriageGetUnionApplyReqMsg.Builder crossMsg = CrossMarriageProto.CrossMarriageGetUnionApplyReqMsg.newBuilder();
        crossMsg.setChildSex(childSex);
        YanQuMessage pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_MARRIAGE_GET_UNION_APPLY, crossMsg);
        GamePlayerMgr.sendPacket(player.getUserId(), pbMsg);
    }

    /**
     * 获取玩家商会联姻数量
     * @param userId
     * @return
     */
    public static int getUserPublishUnionApplyCount(long userId){
        Map<String, MarriageInfo> childMap = marriageInfoMap.get(userId);
        if (null == childMap) {
            return 0;
        }
        int count = 0;
        List<MarriageInfo> childList = new ArrayList<>(childMap.values());
        for (MarriageInfo marriageInfo : childList) {
            if (marriageInfo.getStatus() == eMarriageStatusType.UnionApply) {
                count++;
            }
        }
        return count;
    }

    /**
     * 跨服联姻发布商会后响应
     * @param player
     * @param reqMsg
     */
    public static int crossMarriagePublishUnionResponse(GamePlayer player, CrossMarriageProto.CrossMarriageApplyPublishUnionRespMsg reqMsg){
        MarriageInfo marriageInfo = getAdultChild(player.getUserId(), reqMsg.getChildUid());
        if (null == marriageInfo) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }
        synchronized (locker) {
            if (marriageInfo.getStatus() != eMarriageStatusType.WaitCrossPublish) {//状态不对
                return GameErrorCode.E_CROSS_MARRIAGE_STATE_HAS_CHANGE;
            }
            if(reqMsg.getRet() == 0) {
                marriageInfo.setPublishTime(reqMsg.getPublishTime());
                marriageInfo.setStatus(eMarriageStatusType.UnionApply);
                player.getModule(MarriageModule.class).syncUnMarriageChild(marriageInfo);
                //增加发布日志
                AutoLogMgr.add(new LogPublishMarriageApply(player.getUserId(), marriageInfo.getChildUid(), marriageInfo.getChildName(), true,
                        marriageInfo.getLimitEarnSpeed()));
            }else {
                marriageInfo.setStatus(eMarriageStatusType.WAIT);
            }
        }
        return reqMsg.getRet();
    }

    /**
     * 同意跨服商会联姻
     */
    public static int agreeCrossUnionMarriage(GamePlayer player, String myChildUid, String partnerChildUid) {
        String unionUid = player.getUserInfo().getUnionUid();
        if(StringUtils.isNullOrEmpty(unionUid)){
            return GameErrorCode.E_UNION_NO_JOIN_UNION;
        }
        MarriageInfo child = getAdultChild(player.getUserId(), myChildUid);
        if (null == child) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if (child.getStatus() == eMarriageStatusType.MARRIED) {
            return GameErrorCode.E_MARRIAGE_ALREADY_MARRY;
        }
        if (child.getStatus() == eMarriageStatusType.CrossPublish){
            return GameErrorCode.E_CROSS_MARRIAGE_STATUS_CROSS_PUBLISH;
        }
        if (child.getStatus() == eMarriageStatusType.UnionApply){
            return GameErrorCode.E_CROSS_MARRIAGE_STATUS_CROSS_UNION_PUBLISH;
        }

        CrossMarriageProto.CrossMarriageAgreeCrossDealReqMsg.Builder dealMsg = CrossMarriageProto.CrossMarriageAgreeCrossDealReqMsg.newBuilder();
        dealMsg.setApplyData(MarriagePb.parseMarriageApplyTempMsg(child));
        dealMsg.setPartnerChildUid(partnerChildUid);
        YanQuMessage pbMsg = YanQuMessageUtils.buildMessage(Protocol.C_AGREE_UNION_MARRIAGE_DEAL, dealMsg);
        GamePlayerMgr.sendPacket(player.getUserId(), pbMsg);
        return 0;
    }

    /**
     * 跨服来到联姻对方处理数据
     * @param marriageInfo
     * @param partnerUserId
     * @param partnerChildUid
     * @return
     */
    public static CrossMarriageResult toPartnerDealUnionAgree(MarriageInfo marriageInfo, long partnerUserId, String partnerChildUid){
        CrossMarriageResult marriageResult = new CrossMarriageResult();
        MarriageInfo partnerMarriage = getAdultChild(partnerUserId, partnerChildUid);
        if(null == partnerMarriage){
            marriageResult.setRet(GameErrorCode.E_MARRIAGE_APPLY_NO_FOUND);
            return marriageResult;
        }
        synchronized (locker) {
            if(partnerMarriage.getStatus() != eMarriageStatusType.UnionApply){
                marriageResult.setRet(GameErrorCode.E_MARRIAGE_STATE_HAS_CHANGE);
                return marriageResult;
            }
            partnerMarriage.setStatus(eMarriageStatusType.MARRIED);
        }
        threadTaskManger.addTask(0, new AddCrossMarryTimesTask(partnerUserId, marriageInfo.getUserId(), marriageInfo.getServerId()));
        threadTaskManger.addTask(0, new PartnerMarrySuccessTask(partnerMarriage, marriageInfo, true));
        marriageResult.setMarriageInfo(partnerMarriage);
        return marriageResult;
    }

    /**
     * 跨服同意商会联姻响应
     * @param dealResult
     * @param marriageInfo
     * @param partnerMarriage
     * @return
     */
    public static MarriageResult agreeCrossUnionMarriageResponse(int dealResult, MarriageInfo marriageInfo, MarriageInfo partnerMarriage) {
        MarriageResult marriageResult = new MarriageResult();
        marriageResult.setRet(dealResult);
        if(0 != dealResult){
            return marriageResult;
        }
        GamePlayer player = GamePlayerMgr.getPlayer(marriageInfo.getUserId());
        if(null != player) {
            MarriageInfo myChild = getAdultChild(marriageInfo.getUserId(), marriageInfo.getChildUid());
            if(null != myChild) {
                marriageResult = marrySuccess(player, myChild, partnerMarriage, true, true);
            }
        }
        return marriageResult;
    }

    /**
     * 取消跨服商会联姻
     */
    public static int crossUnionMarriageCancelResponse(long userId, String childUid) {
        MarriageInfo child = getAdultChild(userId, childUid);
        if (null == child) {
            return GameErrorCode.E_MARRIAGE_CHILD_NO_FOUND;
        }

        if (child.getStatus() == eMarriageStatusType.MARRIED) {
            return GameErrorCode.E_MARRIAGE_ALREADY_MARRY;
        }

        synchronized (locker) {
            if(child.getStatus() == eMarriageStatusType.UnionApply) {
                child.setAppointUserId(0);
                child.setPartnerServerId(0);
                child.setStatus(eMarriageStatusType.WAIT);
            }
        }
        return 0;
    }

    /**
     * 退出商会，清理商会联姻申请
     * @param unionUid
     * @param userId
     */
    public static void unionRemoveMember(String unionUid, long userId){
        if(StringUtils.isNullOrEmpty(unionUid)){
            return;
        }
        Map<String, MarriageInfo> childMap = marriageInfoMap.get(userId);
        if(null == childMap){
            return;
        }
        List<MarriageInfo> syncList = new ArrayList<>();
        List<MarriageInfo> dataList = new ArrayList<>(childMap.values());
        for (MarriageInfo marriageInfo : dataList) {
            if(marriageInfo.getStatus() == eMarriageStatusType.UnionApply){
                synchronized (locker) {
                    if(marriageInfo.getStatus() == eMarriageStatusType.UnionApply) {
                        marriageInfo.setStatus(eMarriageStatusType.WAIT);
                    }
                }
                //通知跨服商会联姻超时
                notifyCrossMarriageOverTime(marriageInfo, true);
                syncList.add(marriageInfo);
            }
        }
        GamePlayer player = GamePlayerMgr.getOnlinePlayer(userId);
        if(null != player){
            player.getModule(MarriageModule.class).syncUnMarriageChild(syncList, false);
        }
    }

    public static ThreadTaskManger getThreadTaskManger() {
        return threadTaskManger;
    }

    /**
    * 描述：黑名单处理指向联姻
    * 作者：zrq
    * 时间：2023/1/3
    * 参数：

    * 返回值：
    **/
    public static void blacklistRemoveAppointMarriage(long userId, long blacklistUserId, long blacklistServerId) {
        if(!appointApplyMap.containsKey(userId)){
            return;
        }
        List<MarriageInfo> partnerChildList = new ArrayList<>(appointApplyMap.get(userId).values());
        List<String> needRemoveList = new ArrayList<>();
        for(MarriageInfo partnerChild : partnerChildList){
            synchronized (locker) {
                if(partnerChild.getUserId() == blacklistUserId && partnerChild.getServerId() == blacklistServerId) {
                    if (partnerChild.getStatus() == eMarriageStatusType.APPLY) {
                        partnerChild.setStatus(eMarriageStatusType.WAIT);
                    }
                    partnerChild.setAppointUserId(0);
                    needRemoveList.add(partnerChild.getChildUid());
                }
            }
        }
        if(needRemoveList.size() > 0) {
            for(String partnerChild : needRemoveList){
                appointApplyMap.get(userId).remove(partnerChild);
            }
            GamePlayer player = GamePlayerMgr.getOnlinePlayer(userId);
            if(null != player) {
                player.getModule(MarriageModule.class).syncMarriageApply(getAppointApply(player.getUserId()), true);
            }
            GamePlayer partnerPlayer = GamePlayerMgr.getOnlinePlayer(blacklistUserId);
            if(null != partnerPlayer){//通知拒绝
                partnerPlayer.getModule(MarriageModule.class).marriageRefuseNotify(needRemoveList);
            }
        }
    }

    /**
     * 上报跨世界联姻
     */
    public static void uploadCrossWorldMarriageInfo(){
        long serverId = ConfigHelper.getLong("serverId");
        int groupCount = CrossUnionServerGroupMgr.getGroupServerCountByServerId(serverId);

        if(groupCount > 1){

            List<MarriageInfo> uploadList = new ArrayList<>();

            synchronized (locker){

                for (Map.Entry<String, MarriageInfo> entry : serverApplyMap.entrySet()) {
                    MarriageInfo info = entry.getValue();
                    if(!isOverApplyTime(info) && info.getStatus() == eMarriageStatusType.CrossWorldPublish){
                        uploadList.add(info);
                    }
                }

            }
            //上报跨服
            CrossMarriageProto.CrossMarriageUploadServerPublishMsg.Builder uploadMsg = CrossMarriageProto.CrossMarriageUploadServerPublishMsg.newBuilder();
            for (MarriageInfo marriageInfo : uploadList) {
                uploadMsg.addApply(MarriagePb.parseMarriageApplyTempMsg(marriageInfo));
            }
            GamePlayerMgr.sendPacket(0, YanQuMessageUtils.buildMessage(Protocol.C_CROSS_WORLD_MARRIAGE_APPLY_PUBLISH_UPLOAD, uploadMsg));

            logger.info("uploadCrossWorldMarriageInfo size {}", uploadList.size());
        }
    }

    /**
     * 跨服商会分组变更
     */
    private void unionGroupChange() {
        long serverId = ConfigHelper.getLong("serverId");
        int groupCount = CrossUnionServerGroupMgr.getGroupServerCountByServerId(serverId);

        if(groupCount > 1){
            synchronized (locker) {
                for (Map<String, MarriageInfo> marriageInfoMap : marriageInfoMap.values()) {
                    for (MarriageInfo data : marriageInfoMap.values()) {
                        if(!isOverApplyTime(data) && (data.getStatus() == eMarriageStatusType.APPLY && data.getAppointUserId() == 0)){
                            data.setStatus(eMarriageStatusType.WAIT);
                            serverApplyMap.remove(data.getChildUid());

                            logger.info("开启商会分组：取消本服联姻请求， 玩家{}", data.getUserId());
                        }
                    }
                }
            }
        }
    }
}
